package hashmap

import (
	"gitee.com/Billcoding/gotypes/list"
	"gitee.com/Billcoding/gotypes/set"
	"sync"
)

//define HashMap type
type Key interface{}
type Value interface{}

//define HashMap struct
type HashMap struct {
	//entries in map
	entries map[Key]Value
	//a sync locker
	mutex sync.Mutex
}

//create new empty HashMap
func NewHashMap() *HashMap {
	entries := make(map[Key]Value)
	return &HashMap{
		entries: entries,
		mutex:   sync.Mutex{},
	}
}

//clear hashMap
func (hashMap *HashMap) Clear() {
	hashMap.entries = make(map[Key]Value)
}

//return hashMap size
func (hashMap *HashMap) Size() int {
	return len(hashMap.entries)
}

//push element
func (hashMap *HashMap) Put(key Key, value Value) *HashMap {
	hashMap.mutex.Lock()
	defer hashMap.mutex.Unlock()
	hashMap.entries[key] = value
	return hashMap
}

//get entry
func (hashMap *HashMap) Get(key Key) Value {
	var v Value
	hashMap.mutex.Lock()
	defer hashMap.mutex.Unlock()
	size := hashMap.Size()
	if size > 0 {
		v = hashMap.entries[key]
	}
	return v
}

//check contains key
func (hashMap *HashMap) Key(key Key) bool {
	return hashMap.Get(key) != nil
}

//check contains value
func (hashMap *HashMap) Value(value Value) bool {
	hashMap.mutex.Lock()
	defer hashMap.mutex.Unlock()
	size := hashMap.Size()
	if size > 0 {
		for _, v := range hashMap.entries {
			if v == value {
				return true
			}
		}
	}
	return false
}

//get Keys set
func (hashMap *HashMap) Keys() *set.HashSet {
	hashSet := set.NewHashSet()
	hashMap.ForEach(func(k Key, v Value) {
		hashSet.Add(k)
	})
	return hashSet
}

//get Values list
func (hashMap *HashMap) Values() *list.ArrayList {
	arrayList := list.NewArrayList()
	hashMap.ForEach(func(k Key, v Value) {
		arrayList.Add(v)
	})
	return arrayList
}

//foreach function
func (hashMap *HashMap) ForEach(fn func(k Key, v Value)) {
	for k, v := range hashMap.entries {
		fn(k, v)
	}
}

//map function
func (hashMap *HashMap) Map(mapFn func(k Key, v Value) (Key, Value)) *HashMap {
	return hashMap.fn(func(k Key, v Value) bool {
		return true
	}, mapFn)
}

//filter function
func (hashMap *HashMap) Filter(filterFn func(k Key, v Value) bool) *HashMap {
	return hashMap.fn(filterFn, func(k Key, v Value) (Key, Value) {
		return k, v
	})
}

//matchAny function
func (hashMap *HashMap) MatchAny(matchAnyFn func(k Key, v Value) bool) bool {
	for k, v := range hashMap.entries {
		if matchAnyFn(k, v) {
			return true
		}
	}
	return false
}

//matchNone function
func (hashMap *HashMap) MatchNone(matchNoneFn func(k Key, v Value) bool) bool {
	for k, v := range hashMap.entries {
		if matchNoneFn(k, v) {
			continue
		}
		return true
	}
	return false
}

//matchAll function
func (hashMap *HashMap) MatchAll(matchAllFn func(k Key, v Value) bool) bool {
	count := 0
	for k, v := range hashMap.entries {
		if matchAllFn(k, v) {
			count++
		}
	}
	return count == hashMap.Size()
}

func (hashMap *HashMap) fn(filterFn func(k Key, v Value) bool, mapFn func(k Key, v Value) (Key, Value)) *HashMap {
	newHashMap := NewHashMap()
	for k, v := range hashMap.entries {
		if filterFn(k, v) {
			newHashMap.Put(mapFn(k, v))
		}
	}
	return newHashMap
}
